#lang racket

(require racket/match)

(define builtins
  `((car . ,car)
    (cdr . ,cdr)
    (cons . ,cons)
    (list . ,list)
    (list . ,list?)
    (null? . ,null?)
    (+ . ,+)
    (- . ,-)
    (* . ,*)
    (/ . ,/)
    (= . ,=)
    (eq? . ,eq?)
    (equal? . ,equal?)
    (not . ,not)))

(define special-forms
  '(lambda if quote and or))

(define (i exp env spec)
  (match exp
    ((? number? n) n)
    ((? boolean? b) b)
    ((? symbol? s)
     (cond ((assoc s env) => cdr)
	   (else (error "unbound variable" s))))
    ((? list? l)
     (if (member (car l) spec)
	 (i-special l env spec)
	 (apply (i (car l) env spec)
		(map (lambda (exp) (i exp env spec)) (cdr l)))))
    (else (error "unknown expression type" exp))))

(define (i-special exp env spec)
  (match exp

    (`(if ,t ,c ,a)
     (if (i t env spec)
	 (i c env spec)
	 (i a env spec))

    (`(lambda (,x) ,b)
     (lambda (y)
       (i b (cons (cons x y) env) (remove x spec))))

    (`(quote ,d)
     d)

    (`(and . ,things)
     (let loop (things things)
       (if (null? things)
	   #t
	   (if (i (car things) env spec)
	       (loop (cdr things))
	       #f))))

    (`(or . ,things)
     (let loop (things things)
       (if (null? things)
	   #f
	   (if (i (car things) env spec)
	       #t
	       (loop (cdr things))))))

    (else (error "Unimplemented special form:" exp))))
